--
--处理玩家进入匹配场
--

local skynet = require "skynet"
local cluster = require "skynet.cluster"
require "skynet.manager"

local configdao = require "configdao"
local timetool = require "timetool"
local filelog = require "filelog"
local tabletool = require "tabletool"
local list = require "list"

require "mjexenum"

--节点名
local Node_name = "mjexnode"

--匹配桌配置
local matchtable_conf = nil 

--陪练起始rid
local base_rid = 2222222

local CMD = {}
local handler = { }
local service = nil

--匹配桌
local match_table = { 
    idle_table = {},    -- 空闲匹配桌
    full_table = {},    -- 满人匹配桌
}

-- 匹配队列
local Match_queue = {         
    balance_factor = {}, -- 均衡因子，让新入队列元素均衡分布在子队列
}

local check_matchqueue_fork = nil   --检测匹配队列中匹配超时的线程

local wait_rolelist = {}    --等待角色列表：rid-->{roledata = roledata,level = level, curtime = curtime,queue_index = queue_index}

local Table_player_num = 2  --桌子开局需要人数
local Min_queue_len = 2     --队列最小长度（满足该长度，才进行匹配,长度为桌子人数的倍数）
local Sub_queue_num = 1     --子队列数
local Match_outtime = 5     --匹配超时时间(超过该时间将强制尝试一次匹配）
local Match_final_outtime = 300 --匹配最终超时时间（超过该时间，将结束匹配）
local Level_queue_num = 3   --不同级别队列数，默认3种

local is_need_check_sameIp = false --是否需要检测同ip

----------------------------------

function CMD.init(caller)
	service = caller -- 这里的caller，是router

    --初始化匹配桌配置
    handler.init_matchtable_conf()

    --初始化匹配队列
    Match_queue:init_matchqueue(Level_queue_num)

    --初始化空闲桌
    for i = 1,Level_queue_num do
        match_table.idle_table[i] = { }
    end

    --开启匹配超时检测
    if not check_matchqueue_fork then
        check_matchqueue_fork = skynet.fork(handler.check_match_outtime)
    end

end

function CMD.match(msg,...)
    local f = CMD[msg]
    if not f then
        return { retcode = EErrCode.ERR_INVALID_REQUEST,retstr = "消息不存在！"}
    end

    return f(...)
end

-------------------------

-----------------------------
-- 队列扩展接口

--
--初始化匹配队列
--level_num 不同级别队列数
--
function Match_queue:init_matchqueue(level_num)
    level_num = level_num or 3

    --创建不同级别队列
    for i = 1,level_num do
        self[i] = {}
        table.insert(self.balance_factor,0) 
    end

    --每种级别队列，再创建4个子队列
    local sub_num = Sub_queue_num 
    for i = 1,level_num do
        for j = 1,sub_num do
            table.insert(self[i],list:new())
        end
    end
end

--
-- 插入值到匹配队列
-- level 队列级别
-- ret 返回加入队列的索引
--
function Match_queue:push_matchqueue(level, rid)
    local sub = self.balance_factor[level] % (#self[level]) + 1
    self[level][sub]:push_right(rid)
    self.balance_factor[level] = self.balance_factor[level] + 1

    return sub
end

--
-- 从匹配队列删除
-- level队列级别
--
function Match_queue:pop_matchqueue(level, sub)
    return Match_queue.pop_left(self[level][sub])
end

--
-- 从匹配队列删除n个,并插入tmplist
--轮次从每个子队列删除
-- level队列级别
--
function Match_queue:pop_n_matchqueue(level, n,tmplist)
    local num = 0
    local i = 0
    local empty_num = 0

    while true do
        local sub = i % (#self[level]) + 1
        local val = Match_queue.pop_left(self[level][sub])
        i = i + 1
        if val then
            table.insert(tmplist,val)
            num = num + 1
            if num == n then
                return n
            end
        else
            empty_num = empty_num + 1
            if empty_num == #self[level] then
                return num
            end
        end
    end
end

--
--获取最短匹配队列，进行桌子分配处理
--
function Match_queue:get_less_matchqueue(level, sub, less_len)
    less_len = less_len or Min_queue_len

    local real_size = self[level][sub]:get_size() or 0
    if real_size < less_len then
        less_len = real_size - real_size % Table_player_num
    end

    local sub_queue = { }
    for i = 1,less_len do
        local val = Match_queue.pop_left(self[level][sub])
        if not val then
            break
        end
        table.insert(sub_queue, val)
    end

    return sub_queue
end

--
--获得队列总长度
--level 队列级别
--
function Match_queue:get_total_size(level)
    local total_size = 0
    for i,queue in pairs(self[level]) do
        total_size = total_size + queue:get_size()
    end

    return total_size
end

--
--从队列查找
--level 队列级别
--sub 子队列序号
--
function Match_queue:find_from_matchqueue(rid , level, sub)
    local left_dis = -1   --左距离
    local right_dis = -1  --右距离
    local pos = -1        --在队列中位置

    if level and sub then
        local subqueue = self[level][sub]
        local first = subqueue.first
        local last = subqueue.last

        --查找
        while first <= last do
            if subqueue[first] == rid then
                pos = first
                break
            elseif subqueue[last] == rid then
                pos = last
                break
            else
                first = first + 1
                last = last - 1
            end
        end

        if pos ~= -1 then
            left_dis = pos - subqueue.first
            right_dis = subqueue.last - pos
        end
        
        return pos,left_dis,right_dis
    end
end

-------

--
--从左边开始移动
--first 第一个移动位置
--last 最后一个移动位置
--dis 移动距离,默认1
--
function Match_queue.shifht_from_left(mqueue, first, last, dis)
    dis = dis or 1
    while first >= last do
        mqueue[first + dis] = mqueue[first]
        first = first - 1
    end
    mqueue.first = mqueue.first + 1
end

--
--从右边开始移动
--first 第一个移动位置
--last 最后一个移动位置
--dis 移动距离,默认1
--
function Match_queue.shifht_from_right(mqueue, first, last, dis)
    dis = dis or 1
    while first <= last do
        mqueue[first - dis] = mqueue[first]
        first = first + 1
    end
    mqueue.last = mqueue.last - 1
end

--
--从右边删除
--值为0的，说明是已删除的元素，继续pop下一个
--
function Match_queue.pop_right(mqueue)
    while true do
        local val = mqueue:pop_right()
        if val == nil or val ~= 0 then
            return val
        end
    end
end

--
--从左边删除
--值为0的，说明是已删除的元素，继续pop下一个
--
function Match_queue.pop_left(mqueue)
     while true do
        local val = mqueue:pop_left()
        if val == nil or val ~= 0 then
            return val
        end
    end
end

--
--从匹配队列删除
--在队列两边：直接删除
--在队列中间：移动元素不超过10个时，移动队列并删除；移动元素超过10个时，将其值置为0，后续再处理
--level 队列级别
--
function Match_queue:remove_from_matchqueue(rid, level)
    local subqueue = self[level]
    local left_dis = -1   --左距离
    local right_dis = -1  --右距离
    local pos = -1        --在队列中位置

    wait_rolelist[rid] = nil
    for i,queue in pairs(subqueue) do
        local size = queue:get_size()
        if size > 0 then
            if queue[queue.last] == rid then
                queue:pop_right()
                return true
            end

            if queue[queue.first] == rid then
                queue:pop_left()
                return true
            end

            --在子队列按序查找
            pos,left_dis,right_dis =  Match_queue:find_from_matchqueue(rid, level, i) 
            local move_num = left_dis 
            if pos ~= -1 then
                if right_dis < move_num then
                    move_num = right_dis
                end

                --移动数量不大于1000，移动
                if move_num <= 1000 then
                    if move_num == left_dis then    --左移
                        Match_queue.shifht_from_left(queue,pos - 1,queue.first)
                    else    --右移
                        Match_queue.shifht_from_right(queue,pos + 1,queue.last)
                    end
                    queue.size = queue.size - 1
                else
                    queue[pos] = 0
                    --此时队列长度并没有减1
                end
                return true
            end
        end
    end
    
    return false
end

----------------------------

local function swap(arr,i,j)
    local tmp = arr[j]
    arr[j] = arr[i]
    arr[i] = tmp
end

--
--初始化匹配桌配置
--
function handler.init_matchtable_conf()
    matchtable_conf = tabletool.deepcopy(configdao.get_common_conf("matchtable_conf"))
    if not matchtable_conf then
        matchtable_conf = {}
        Level_queue_num = 0
        return
    end

    local cnt = 0
    for k,v in pairs(matchtable_conf) do
        cnt = cnt + 1
    end
    Level_queue_num = cnt
end

--
--通知匹配结果
--
function handler.send_match_result_to_player(rids,message,is_new_format)
    local retcode,retstr,msgbody
    if message and message.retcode  then
		retcode = tonumber(message.retcode)
		retstr = message.retstr or ""
		msgbody = message.msgbody or { }
    else
        msgbody = message
	end

    local rids = rids
    local msgname = "matchresultN"
    local nodename = skynet.getenv("nodename") or Node_name
    nodename = string.sub(nodename,1, string.len(nodename) - 4)

    local status = false
    if is_new_format then
        local tmp = { msgname = msgname, msgbody = json.encode(message)}
        status = skynet.pcall(cluster.send,"gatenode", ".router", "notify", rids, nodename .. ".notifyN", tmp, retcode, retstr)
    else
	    status = skynet.pcall(cluster.send,"gatenode", ".router", "notify", rids, nodename .. "." ..msgname, msgbody, retcode, retstr)
    end
    if not status then
        filelog.sys_error("notice player failed!")
    end
end

--
--通知gate，开始匹配
--
function handler.notice_gate_startmatch(rid)
    local nodename = skynet.getenv("nodename") or Node_name
    nodename = string.sub(nodename,1, string.len(nodename) - 4)
    skynet.pcall(cluster.send,"gatenode", ".router", "role_start_match", rid, nodename)
end

--
--通知gate，结束匹配
--
function handler.notice_gate_endmatch(rid)
    local nodename = skynet.getenv("nodename") or Node_name
    nodename = string.sub(nodename,1, string.len(nodename) - 4)
    skynet.pcall(cluster.send,"gatenode", ".router", "role_end_match", rid, nodename)
end

--
--加入匹配桌
--自动坐下，准备
--
function handler.do_sitdown_and_ready(table_service,table_index,rid,roledata)

    --坐下
    skynet.pcall(skynet.call,table_service, "lua", "request", "sitdowntable", rid, roledata,{ })

    --准备
    skynet.pcall(skynet.call,table_service, "lua", "request", "gameready", rid, roledata,{ table_index = table_index})
end

--
--获得空闲匹配桌
--level 匹配桌级别（1初级 2中级 3高级）
-- nums 获得空闲桌数量,默认1
--
function handler.get_idle_matchtable(level,nums)
    nums = nums or 1

    local idletable = { }
    if nums <= 0 then
        return idletable
    end
    
    local num = 0
    for k,v in pairs(match_table.idle_table[level]) do
        match_table.idle_table[level][k] = nil
        if service.index_to_table[k] and not match_table.full_table[k] then
            table.insert(idletable,k)
            num = num + 1
            if num >= nums then
                break
            end
        end
    end

    --桌子不够，创建匹配桌，默认创建10个
    if num < nums then
        CMD.create_some_match_table(level,nums - num)
        for k,v in pairs(match_table.idle_table[level]) do
            table.insert(idletable,k)
            num = num + 1
            match_table.idle_table[level][k] = nil
            if num >= nums then
                break
            end
        end
    end

    return idletable
end

--
--检测所有玩家状态（是否已在桌子，是否超时）
--
function handler.check_allplayers_state(level, match_queue)
    local curtime = timetool.get_time()
    local lasttime = nil
    local real_matchqueue = { }

    local enter_diamond = matchtable_conf[level].enter_diamond
    for i,rid in pairs(match_queue) do
        local status,is_error = skynet.pcall(cluster.call,"gatenode",".router","check_role_state",rid,enter_diamond)
        if not status then
            filelog.sys_error("call gate error!")
            break
        end

        --不在桌子内，且未超时，才将其加入实际的匹配队列
        lasttime = 0
        if wait_rolelist[rid] then
            lasttime = wait_rolelist[rid].curtime
        else
            is_error = true
        end

        if not is_error and (curtime - lasttime < Match_final_outtime) then
            table.insert(real_matchqueue,rid)
        else
            --通知gate，结束匹配
            handler.notice_gate_endmatch(rid)
            wait_rolelist[rid] = nil
            filelog.sys_error("match failed!,error state!!!", rid)
        end
    end
    
    return real_matchqueue
end

--
--检测同ip玩家
--
function handler.check_same_ip_players(match_queue, same_ip_players)
    local player_num = Table_player_num
    local real_matchqueue = { }

    local ips = { }
    local len = #match_queue
    local cur_num = 0
    for i = 1,len  do
        local rid = match_queue[i]
        local roledata = wait_rolelist[rid].roledata
        local ip_addr = roledata.ip_addr
        filelog.print("----ip_addr", rid, ip_addr)
        if ip_addr and not ips[ip_addr] then
            ips[ip_addr] = true
            table.insert(real_matchqueue,rid)
            cur_num = cur_num + 1
            if cur_num == player_num then
                len = #match_queue
                ips = { }
                cur_num = 0
            end
        else
            table.insert(match_queue,rid)
        end
    end
    
    for i = len + 1,#match_queue do
        table.insert(same_ip_players,match_queue[i])
    end

    match_queue = real_matchqueue
    real_matchqueue = { }
    for i = 1,#match_queue do
        local rid = table.remove(match_queue)
        table.insert(real_matchqueue,rid)
    end

    return real_matchqueue
end

--
--处理一个匹配成功队列
--level 匹配桌级别（1初级 2中级 3高级）
--match_queue 待处理队列
--
function handler.handle_matchqueue(level,match_queue)
    local player_num = Table_player_num

    filelog.print("-----start match------")

    --检测所有匹配玩家状态，踢出无效的玩家
    match_queue = handler.check_allplayers_state(level, match_queue)

    --打乱玩家顺序,玩家多于一桌才进行
    local first = 1
    local last = #match_queue
    if last > player_num then
        for i = first + 1,last do
            swap(match_queue, i, first + math.random(0,i - first))
        end
    end

    --检测同IP
    local same_ip_players = { }
    if is_need_check_sameIp then
         match_queue = handler.check_same_ip_players(match_queue, same_ip_players)
    end

    --依次加入空闲桌
    last = #match_queue
    local table_num = math.floor(last / player_num)
    local idletable = handler.get_idle_matchtable(level,table_num)
    table_num = #idletable
    for i = 1,table_num do
        local table_index = table.remove(idletable)
        local table_service = service.index_to_table[table_index].table_service
        local rids = { }
        for j = 1,player_num do
            local rid = table.remove(match_queue)
            local roledata = wait_rolelist[rid].roledata
            table.insert(rids,rid)

            --通知gate，结束匹配
            handler.notice_gate_endmatch(rid)

            --进桌
            local status,result = skynet.pcall(skynet.call,table_service, "lua", "request", "entertable", rid, roledata, {is_auto = true})
	        if result and result.retcode == nil then
                service:forward(rid, table_service, table_index,roledata.money.diamond)
            else
                result = {retcode = EErrCode.ERR_INVALID_REQUEST,retstr = "没有空的匹配桌"} 
            end
            wait_rolelist[rid] = nil

            --自动坐下和准备
            handler.do_sitdown_and_ready(table_service,table_index,rid, roledata)
        end
        --通知客户端匹配结果
        handler.send_match_result_to_player(rids,{table_index = table_index})

        match_table.full_table[table_index] = {table_index  = table_index,level =level}
    end

    --如果匹配队列还有人，尝试一次超时匹配
    if #match_queue > 0 then
        handler.handle_all_outtime_match({[level] = match_queue})
    end

    --将同ip玩家再加入匹配队列
    for i = 1,#same_ip_players do
        local rid = same_ip_players[i]
        Match_queue:push_matchqueue(level, rid)
    end
    
end

--
--添加陪练到匹配桌(测试用，加入机器人）
--
function handler.add_peilian_to_matchtable(num,matchlist,level)
    if true then
        return false
    end

    --加入机器人
    local curtime = timetool.get_time()
    for i = 1,num do
        base_rid = base_rid + 1
        local rid = base_rid
        local roledata = {
            info = { 
                rolename = "陪练" .. (rid % 100000), --名字
                level = 0,              --等级
		        logo = "1",             --头像
		        sex = 2,                --性别
                rid = rid,
            },
            money = {
                diamond = 1000000,
                fangka = 1000,
                coin = 100000,
                dou = 100000,
                lock_diamond = 0,
            },
            is_robot = true,
        }

        wait_rolelist[rid] = { roledata = roledata,curtime = curtime,level = level}
        table.insert(matchlist,rid)
    end

    return true
end

--
--获得所有超时匹配
--
function handler.get_all_outime_match(match_queue)
    local out_time = Match_outtime     --匹配超时时间
    local wait_time = Match_outtime    --循环检测线程默认间隔时间
    local curtime = timetool.get_time()
    local outtime_list = { }

    --检测超时匹配
    for i,v in pairs(match_queue) do
        local cur_queue = match_queue[i]
        local size = cur_queue:get_size()

        if size > 0 then
            for j = cur_queue.first,cur_queue.last do
                local rid = cur_queue[j]
                if rid == 0 then
                    cur_queue:pop_left()
                else
                    local lasttime = wait_rolelist[rid].curtime
                    if curtime - lasttime >= Match_final_outtime then  --超过最终超时时间，踢出匹配队列
                        CMD.cancelmatch(rid)
                    elseif curtime - lasttime >= out_time then --超过5s还未匹配成功，立即处理（加入机器人或陪打）
                        local val = cur_queue:pop_left()
                        table.insert(outtime_list,val)
                    else
                        local w = out_time - (curtime - lasttime)
                        if w < wait_time then
                            wait_time = w
                        end
                        break
                    end
                end
            end
        end
    end

    return outtime_list,wait_time
end

--
--处理所有超时匹配
--
function handler.handle_all_outtime_match(outtime_list)
    local player_num = Table_player_num
    
    --处理超时匹配
    for i,curlist in pairs(outtime_list) do
        local len =  #curlist
        if len > 0 then
            local need_peilian_num = player_num - (len % player_num)

            --玩家不足，先从匹配队列加入，不够再加入陪练
            if need_peilian_num < player_num then   
                local curqueue = Match_queue[i]
                local size = Match_queue:get_total_size(i)
                local realnum = 0

                if size >= need_peilian_num then
                    realnum = Match_queue:pop_n_matchqueue(i,need_peilian_num,curlist)
                    need_peilian_num = need_peilian_num - realnum
                elseif size > 0 then
                    realnum = Match_queue:pop_n_matchqueue(i,size,curlist)
                    need_peilian_num = need_peilian_num - realnum
                end

                local is_ok = true
                if need_peilian_num > 0 then
                    Match_queue.balance_factor[i] = 0 --重置均衡因子
                    is_ok = handler.add_peilian_to_matchtable(need_peilian_num,curlist,i)
                end
                if not is_ok then   --失败，多余的人重新加入匹配队列,并更新匹配时间
                    local cnt = (#curlist) % player_num
                    for k = 1,cnt do
                        local rid = table.remove(curlist)
                        Match_queue:push_matchqueue( i, rid)
                    end
                end
                --进行匹配
                if #curlist > 0 then
                    handler.handle_matchqueue(i,curlist)
                end
            else
                handler.handle_matchqueue(i,curlist)
            end
        end 
    end

end

--
--检测匹配超时
--超过一定时间仍在匹配队列的玩家，立即对其进行匹配处理
--
function handler.check_match_outtime()
    local level_num = Level_queue_num
    local outtime_list = nil
    local wait_time = 0    --循环检测线程默认间隔时间

    while true do
        --filelog.print("----check-----start---")
        wait_time = Match_outtime
        outtime_list = { }

        --检测每一个子队列，剔除所有超时的匹配
        for i = 1,level_num do
            local status,rids,wt = pcall(handler.get_all_outime_match,Match_queue[i])
            if status then
                outtime_list[i] = rids
                if wait_time > wt then
                    wait_time = wt
                end
            else
                filelog.sys_error("error----",status, rids,wt)
            end
        end

        --处理超时匹配
        --filelog.print("outtime_list",outtime_list)
        local status,err = pcall(handler.handle_all_outtime_match,outtime_list)
        if not status then
            filelog.sys_error("error----handle_all_outtime_match",status,err,outtime_list)
        end

        --filelog.print("----check-----end---",wait_time)

        --挂起一段时间，然后继续检测
        skynet.sleep(wait_time * 100)

    end

end

--
--开始匹配桌子
--加入匹配队列，等待处理
--
function handler.start_match_table(rid, roledata, match_type)
    if not rid or rid < 1 or not roledata or not roledata.money or match_type < 1 or match_type > Level_queue_num then
        return {retcode = EErrCode.ERR_INVALID_PARAMS,retstr = "无效的请求"}
    end

    --检测余额是否足够
    local diamond = roledata.money.diamond or 0
    if diamond < matchtable_conf[match_type].enter_diamond then
        return {retcode = EErrCode.ERR_INVALID_PARAMS,retstr = "余额不足，不能继续游戏"}
    end

    local less_len = Min_queue_len --匹配队列最短长度
    local curtime = timetool.get_time()

    --加入匹配队列
    wait_rolelist[rid] = {roledata = roledata, curtime = curtime, level = match_type}
    local queue_index = Match_queue:push_matchqueue( match_type, rid)
    local queue_size = Match_queue[match_type][queue_index]:get_size()
    wait_rolelist[rid].queue_index = queue_index
    filelog.print("add to queue",queue_index,rid,queue_size)

    --通知gate，开始匹配
    handler.notice_gate_startmatch(rid)

    --队列人数满足最小要求，则处理
    if queue_size >= less_len then
        local sub_queue = Match_queue:get_less_matchqueue( match_type, queue_index, less_len)
        skynet.fork(handler.handle_matchqueue, match_type, sub_queue)
    end

    return {timeout_time = curtime + Match_final_outtime}
end

----------------------------------
--外部接口

--
--将桌子加入空闲匹配桌列表
--
function CMD.add_to_idle_match_table(table_index,level)
    match_table.full_table[table_index] = nil
    if service.index_to_table[table_index] then
        match_table.idle_table[level][table_index] = table_index
    else
        match_table.idle_table[level][table_index] = nil
    end
end

--
--将桌子从满匹配桌列表删除
--
function CMD.del_from_full_match_table(table_index,level)
    match_table.full_table[table_index] = nil
end

--
--创建匹配场
--
function CMD.create_match_table(level,table_conf,roledata)
    level = level or 1
    table_conf = table_conf or matchtable_conf[level]
    if not table_conf then
        table_conf = {
            table_type = EMJTableType.TABLE_TYPE_ZIJIAN_ZUANSHI,
            max_player_num = Table_player_num,
            is_public = 0,
            score_diamond = 100,
            enter_diamond = 3000,
            leave_diamond = 1500,
        }
    end

    if not roledata then
        roledata = {
            info = { 
                rolename = "root",  --名字
                level = 0,          --等级
		        logo = "1",         --头像
		        sex = 2,            --性别
                rid = 22222222,
            },
            money = {
                diamond = 99999999,
                fangka = 9999999,
                coin = 99999999,
                dou = 99999999,
                lock_diamond = 0,
             },
            table_persist = 1800,   --保留30分钟
            ip_addr = "",
        }
    end

    local func = service.CMD.createtable
    local msgbody = {table_conf = table_conf,is_matchtable = true,match_type = level}

    return func(roledata.info.rid,roledata,msgbody)
end

--
--创建一些匹配桌
--tablenum 创建数量
--
function CMD.create_some_match_table(level,tablenum)
    tablenum = (tablenum or 1) * 2
    if tablenum <= 0 then
        return
    end

    local idle_match_table = match_table.idle_table[level]
    local table_conf = matchtable_conf[level]
    for i = 1,tablenum do
        local new_table = CMD.create_match_table(level,table_conf)
        if new_table and new_table.table_index then
            idle_match_table[new_table.table_index] = new_table.table_index
        end
    end
end

--
--检测是否为匹配桌
--
function CMD.check_is_matchtable(table_index)
    if not table_index or table_index < 1 or 
    not service.index_to_table[table_index] then
        return false
    end

    if match_table.full_table[table_index] then
        return true
    end

    for i,idle_table in pairs(match_table.idle_table) do
        if idle_table[table_index] then
            return true
        end
    end
    
    return false
end

--
--更新匹配超时时间
--
function CMD.update_match_outtime(new_time)
    if not new_time or type(new_time) ~= "number" or new_time < 3 then
        return 
    end
    Match_outtime = new_time
end

--
--更新最小队列长度
--
function CMD.update_min_queue_len(new_len)
    if not new_len or type(new_len) ~= "number" 
    or new_len < Table_player_num or new_len % Table_player_num ~= 0 then
        return 
    end
    Min_queue_len = new_len
end

--
--获得匹配桌信息
--
function CMD.get_matchlist()
    local matchlist = { }

    for i,conf in pairs( matchtable_conf) do
        matchlist[i] = {
            match_type 	    = i,	                        --匹配场类型 1初级 2中级 3高级（级别越高底注越高）
	        dizhu  		    = conf.score_diamond,	        --底注
	        ruchang		    = conf.enter_diamond, 	        --入场钻石
	        lichang		    = conf.leave_diamond,	        --离场钻石
	        conf_intro      = tabletool.deepcopy(conf),     --匹配桌配置
	        max_player      = conf.max_player_num,	        --桌子最大人数                         
        }

        --该匹配场当前总人数：游戏中 + 匹配中
        matchlist[i].cur_total_player =  Match_queue:get_total_size(i)
    end

    --获得游戏中人数
    for k,item in pairs(match_table.full_table) do
        local i = item.level
        if service.index_to_table[k] then
            matchlist[i].cur_total_player = matchlist[i].cur_total_player + matchlist[i].max_player
        else
            match_table.full_table[k] = nil
            match_table.idle_table[i][k] = nil
        end
    end
    
    return matchlist
end

--
--重新开始匹配
--
function  CMD.restart_matchtable(rids,match_type,is_new_format)
    if not rids or #rids < 1 then
        return
    end

    --由客户端再次发送匹配请求
    local msgname = "restartmatchN"
    local nodename = skynet.getenv("nodename") or Node_name
    nodename = string.sub(nodename,1, string.len(nodename) - 4)

    local msgbody = { match_type = match_type,nodename = nodename}
    local status = false
    if is_new_format then
        local tmp = { msgname = msgname, msgbody = json.encode(msgbody)}
        status = skynet.pcall(cluster.send,"gatenode", ".router", "notify", rids, nodename .. ".notifyN", tmp)
    else
	    status = skynet.pcall(cluster.send,"gatenode", ".router", "notify", rids, nodename .. "." .. msgname, msgbody)
    end
    if not status then
        filelog.sys_error("notice player failed!")
    end
end

--
--进入匹配桌
--
function CMD.entermatchtable(rid, roledata, request)
    --设置停服标记后，不能进入匹配
    if service.gameend_flag then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    if wait_rolelist[rid] ~= nil then
        return {retcode = 0,retstr = ""}
    end

    --开始匹配
    roledata.gps_addr = request.gps_addr
    local level = request.match_type or 1
    local result = handler.start_match_table(rid, roledata, level)

	return result
end

--
--取消匹配
--
function CMD.cancelmatch(rid, roledata, request)
    if wait_rolelist[rid] == nil then
        return {retcode = 0, retstr = ""}
    end

    --取消匹配
    local level = wait_rolelist[rid].level
    local result = Match_queue:remove_from_matchqueue(rid, level)
    if not result then
        return {retcode = 0, retstr = ""}
    end

    --通知gate，结束匹配
    handler.notice_gate_endmatch(rid)

	return {}
end

--
--重新进入匹配桌
--
function CMD.reentermatchtable(rid, roledata, request)
    if wait_rolelist[rid] == nil then
        handler.notice_gate_endmatch(rid)
        return {}
    end

    local level = wait_rolelist[rid].level
    
    --检测余额是否足够
    local diamond = roledata.money.diamond or 0
    if diamond < matchtable_conf[level].enter_diamond then
        CMD.cancelmatch(rid,roledata, request)
        return {retcode = EErrCode.ERR_INVALID_PARAMS,retstr = "余额不足，不能继续游戏"}
    end

    --更新roledata
    local responsemsg = { }
    roledata.gps_addr = request.gps_addr
    wait_rolelist[rid].roledata = roledata
    responsemsg.match_type = level
    responsemsg.timeout_time = wait_rolelist[rid].curtime + Match_final_outtime

	return responsemsg
end

--
--获得等待玩家列表
--
function CMD.get_wait_rolelist()
    local result = tabletool.deepcopy(wait_rolelist)
    local matchqueue = { }
    for i = 1,Level_queue_num do
        matchqueue[i] = tabletool.deepcopy(Match_queue[i])    
    end
  
    return {matchqueue,result}
end

--
--重置匹配队列
--
function CMD.reset_matchqueue()
    wait_rolelist = wait_rolelist or {}
    for rid,item in pairs(wait_rolelist) do
        CMD.cancelmatch(rid,item.roledata)
    end

    --匹配桌
    match_table = { 
        idle_table = {},    -- 空闲匹配桌
        full_table = {},    -- 满人匹配桌
    }

    -- 匹配队列
    Match_queue.balance_factor = { }

    wait_rolelist = { }
    
    
    --初始化匹配队列
    Match_queue:init_matchqueue(Level_queue_num)

    --初始化空闲桌
    for i = 1,Level_queue_num do
        match_table.idle_table[i] = { }
    end
end

--
--获得玩家所在匹配队列已匹配人数
--
function CMD.getmatchnum(rid, roledata, request)
    local cur_num = 1
    local table_player_num = Table_player_num - 1

    if rid and wait_rolelist[rid] then
        local level = wait_rolelist[rid].level or 0
        local sub_index = wait_rolelist[rid].queue_index or 0

        if Match_queue[level] and Match_queue[level][sub_index] then
            local subqueue = Match_queue[level][sub_index]
            local size = subqueue:get_size()
            if not is_need_check_sameIp then
                if size > table_player_num  then
                    size = table_player_num
                end
                return { cur_match_num = size}
            end

            local tmpqueue = { }
            local is_find = false
            for i = subqueue.first,subqueue.last do
                table.insert(tmpqueue,subqueue[i])
                if rid == subqueue[i] then
                    is_find = true
                end
            end

            local same_ip_players = { }
            if is_find then
                tmpqueue = handler.check_same_ip_players(tmpqueue,same_ip_players)
                for _,v in pairs(tmpqueue) do
                    if v == rid then
                        cur_num = #tmpqueue
                        break
                    end
                end
            end
        end
    end

    if cur_num > table_player_num then
        cur_num = table_player_num
    elseif cur_num == 0 then
        cur_num = 1
    end

    return { cur_match_num = cur_num}
end

--
--获得匹配队列长度
--
function CMD.get_matchqueue_len(level)
    local total = 0
    if level then
        if level > 0 and level <= Level_queue_num then
            return Match_queue:get_total_size(level)
        end
        return nil
    else
        for i = 1,Level_queue_num  do
            total = total + Match_queue:get_total_size(i)
        end
    end
    return total
end

--
--获得匹配场信息
--
function CMD.get_matchinfo()
    local match_infos = {}
    for i,conf in pairs( matchtable_conf) do
        match_infos[i] = { 
            match_level = i,
            base_score = conf.score_diamond,
            enter_score = conf.enter_diamond,
            max_player = conf.max_player_num,
        }
        match_infos[i].wait_num = Match_queue:get_total_size(i)
    end

    return match_infos
end

return CMD

